package net.sudot.chess.util;

import org.apache.commons.io.IOUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.retry.RetryContext;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 玩呗房间链接解析
 *
 * @author tangjialin on 2018-07-07.
 */
public class WBihuizhiUtils {
    private static final Logger LOGGER = LoggerFactory.getLogger(WBihuizhiUtils.class);

    /** 玩呗房号URL规则 */
    private static final Pattern IHUIZHI_URL_PATTERN = Pattern.compile("^https?://zj\\.emenu\\.cn/chess/[\\w/]+");
    /** 玩呗房号微信消息规则 */
    private static final Pattern IHUIZHI_ROOM_SN_WECHAT_MESSAGE_PATTERN = Pattern.compile("(\\d{6})");
    /** 游戏开房指令 */
    public static final String OPEN_ROOM_COMMAND = "开房";
    private static final RestTemplate REST_TEMPLATE = new RestTemplate(Collections.singletonList(new ByteArrayHttpMessageConverter()));

    /**
     * 验证URL是否正确
     *
     * @param url URL地址
     * @return 返回验证结果
     */
    public static boolean validateUrl(String url) {
        return url != null && !url.isEmpty() && IHUIZHI_URL_PATTERN.matcher(url).matches();
    }

    /**
     * 获取玩呗房间链接对于的HTML内容
     *
     * @param url 玩呗房间链接
     * @return 返回该房间HTML内容
     */
    public static String getHtml(String url) {
        if (url == null || url.isEmpty() || !IHUIZHI_URL_PATTERN.matcher(url).matches()) {
            throw new IllegalArgumentException("房间链接格式错误");
        }
        try {
            RetryTemplate retryTemplate = new RetryTemplate();
            // 由retryTemplate 执行execute方法开始逻辑执行
            return retryTemplate.execute(new RetryCallback(url));
        } catch (Exception e) {
            LOGGER.error("房间页面获取失败", e);
            throw new IllegalArgumentException("房间页面获取失败");
        }
    }

    /**
     * 解析房号
     *
     * @param html 解析的html文件内容
     * @return 返回房号
     */
    public static String getRoomSn(String html) {
        Elements roomNumberElements = Jsoup.parse(html).getElementsByClass("room-number");
        if (roomNumberElements.isEmpty()) { return null; }
        Elements numberElements = roomNumberElements.first().getElementsByClass("number");
        if (numberElements.isEmpty()) { return null; }
        return numberElements.first().text();
    }

    /**
     * 解析房号
     *
     * @param content 解析的微信信息标题
     * @return 返回房号
     */
    public static String getRoomSnByWechatMessage(String content) {
        Matcher matcher = IHUIZHI_ROOM_SN_WECHAT_MESSAGE_PATTERN.matcher(content);
        if (matcher.find()) {
            return matcher.group(1);
        }
        return null;
    }

    /**
     * 解析房间状态
     *
     * @param html 解析的html文件
     * @return 返回房间状态
     */
    public static RoomStatus getRoomStatus(String html) {
        Document document = Jsoup.parse(html);
        Elements roomNumberElements = document.getElementsByClass("starting");
        String status = null;
        if (roomNumberElements.isEmpty()) {
            // 链接地址已经过期，有效期3小时!
            status = Optional.ofNullable(document.getElementsByTag("p").first()).map(Element::text).orElse(null);
        } else {
            // 未开始
            // 进行中
            // 已结束
            status = Optional.ofNullable(roomNumberElements.select(".col .fs18"))
                    .map(elements -> elements.first().text()).orElse(null);
        }
        return RoomStatus.valueOfByMemo(status);
    }

    public enum RoomStatus {
        /** */ WAIT("未开始"),
        /** */ PLAYING("进行中"),
        /** */ END("已结束"),
        /** */ CANCEL("已解散"),
        /** */ EXPIRED("链接地址已经过期，有效期3小时!"),
        ;

        private String memo;

        RoomStatus(String memo) {
            this.memo = memo;
        }

        public String memo() {
            return memo;
        }

        public static RoomStatus valueOfByMemo(String memo) {
            if (memo == null || memo.isEmpty()) { return null; }
            for (RoomStatus status : RoomStatus.values()) {
                if (status.memo().equals(memo)) { return status;}
            }
            return null;
        }
    }

    private static class RetryCallback implements org.springframework.retry.RetryCallback<String, Exception> {
        private String url;

        public RetryCallback(String url) {
            this.url = url;
        }

        /**
         * 通过RetryCallback 重试回调实例包装正常逻辑逻辑，第一次执行和重试执行执行的都是这段逻辑
         *
         * @param context 重试操作上下文约定，统一spring-try包装
         * @return
         * @throws Exception
         */
        @Override
        public java.lang.String doWithRetry(RetryContext context) throws Exception {
            return doExecute(url);
        }

        /**
         * 抓取URL对应的响应内容,并处理重定向
         *
         * @param url 抓取的URL
         * @return 返回URL返回的内容
         */
        public String doExecute(String url) {
            return REST_TEMPLATE.execute(url, HttpMethod.GET, null, response -> {
                if (response.getStatusCode().is3xxRedirection()) {
                    return doExecute(response.getHeaders().getLocation().toString());
                }
                return IOUtils.toString(response.getBody(), StandardCharsets.UTF_8);
            });
        }
    }

}
